package com.diaohaiwen.framework.db.filter;

import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.druid.filter.FilterChain;
import com.alibaba.druid.filter.FilterEventAdapter;
import com.alibaba.druid.proxy.jdbc.JdbcParameter;
import com.alibaba.druid.proxy.jdbc.PreparedStatementProxy;
import com.alibaba.druid.proxy.jdbc.StatementProxy;
import com.alibaba.fastjson.JSONObject;
import com.diaohaiwen.common.log.BaseLogSend;
import com.diaohaiwen.common.log.LogProperties;
import com.diaohaiwen.common.enums.StatusCodeEnum;
import com.diaohaiwen.common.obj.cache.RequestCache;
import com.diaohaiwen.common.obj.log.DbLog;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * @author 刁海文
 * @Remark <a href="https://blog.csdn.net/m0_38005658/article/details/90699186">参考地址</a>
 * @since 2023/6/28 22:07
 */
@Component
public class DruidAdapter extends FilterEventAdapter {

    private final Logger logger = LoggerFactory.getLogger("Db-Filter");

    @Resource
    LogProperties logProperties;

    @Autowired(required = false)
    BaseLogSend baseLogSend;

    @Override
    protected void statementExecuteAfter(StatementProxy statement, String sql, boolean result) {
        super.statementExecuteAfter(statement, sql, result);
    }

    @Override
    public boolean preparedStatement_execute(FilterChain chain, PreparedStatementProxy statement) throws SQLException {
        if (!logProperties.isDb() || baseLogSend == null) {
            return chain.preparedStatement_execute(statement);
        }

        DbLog log = new DbLog();
        try {
            log.setSqlContent(statement.getRawObject().toString())
                    .setSqlTemplate(statement.getSql())
                    .setRequestTime(LocalDateTime.now())
                    .setRequestId(RequestCache.getThreadRequestId())
                    .setLinkId(RequestCache.getThreadLinkId())
                    .setApplicationName(SpringUtil.getApplicationName())
                    .setIp(RequestCache.LOCAL_IP)
            ;

            if (RequestCache.isLogin()) {
                log.setLoginId(RequestCache.getThreadLocal().getLoginId())
                        .setLoginName(RequestCache.getThreadLocal().getLoginName());
            }
            //抽象方法
            statementExecuteBefore(statement, statement.getSql());
            // 执行SQL
            boolean execute = chain.preparedStatement_execute(statement);
            // 获取入参
            JSONObject params = new JSONObject();
            for (Map.Entry<Integer, JdbcParameter> entry : statement.getParameters().entrySet()) {
                params.put(entry.getKey().toString(), entry.getValue().getValue());
            }
            log.setSqlParameter(params.toJSONString());
            // 非select 语句时可获取更改的行数
            log.setSqlRowCount(statement.getUpdateCount());
            // 获取SQL执行类型
            if (log.getSqlRowCount() == -1) {
                log.setSqlType("SELECT");
            } else if (log.getSqlContent().startsWith("INSERT")) {
                log.setSqlType("INSERT");
            } else if (log.getSqlContent().startsWith("UPDATE")) {
                log.setSqlType("UPDATE");
            } else if (log.getSqlContent().startsWith("DELETE")) {
                log.setSqlType("DELETE");
            } else {
                log.setSqlType("OTHER");
            }

            //抽象方法，后面我们会重写这个方法
            this.statementExecuteAfter(statement, statement.getSql(), execute);

            log.setStatus(StatusCodeEnum.SUCCESS.status);
            log.setMsg(StatusCodeEnum.SUCCESS.describe);
            return execute;

        } catch (SQLException | RuntimeException | Error error) {
            // SQL执行异常
            log.setStatus(StatusCodeEnum.EXCEPTION.status);
            log.setMsg(StatusCodeEnum.EXCEPTION.filterAttr(error.getMessage()));

            //抽象方法
            statement_executeErrorAfter(statement, statement.getSql(), error);
            throw error;
        } finally {
            log.setResponseTime(LocalDateTime.now())
                    .computeElapsed();
            log.setLogTime(LocalDateTime.now());
            baseLogSend.executeDB(log);
        }

    }

}
